<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style type="text/css">
			* {
				margin: 0;
				padding: 0;
			}

			html,
			body {
				width: 100%;
				height: 100%;
			}

			body {
				background-color: #5e5e5e;
			}

			#myCanvas {
				position: fixed;
				left: 0;
				top: 0;
				width: 100%;
				height: 100%;
				z-index: 1;
			}

		</style>
	</head>
	<body>
		<canvas id="myCanvas" width="800" height="800"></canvas>
		<script type="text/javascript" src="tCanvas.js"></script>
		<script type="text/javascript">
			var pDemo = new Particle({
				mode: 9,
				mode9(){
					var mdc = {
						k: 50,				//弹力系数( 推荐值 5 )
						lmt: 2,				//作用阈值/px( 绳长,从外界传入,作为全局变量 )
						mass: 1,			//重力加速度( 下坠作用强度 推荐值 2 )
						dampAll: .1,		//全局最低阻力( 推荐值0.1 )
						dampM: .8,			//阻力倍增系数( 最低阻力的基础上, 正比于当前速度的模 )( 值越大, 运动越平缓, 推荐值 0.7 )
					}
					var pList = []
					var mouse = {
						pos: this.mouse
					}
					for(let i = 0; i < 20; i++){
						pList.push({
							pos: [0,0],
							step: [0,0],
							retain: [0,0],
							damp: [0,0]
						})
					}
					this.canvas.style.pointerEvents = "none"
					let flag = false
					let calc = () => {
						this.cx.fillStyle = "#fff"
						for(let i = 0; i < pList.length; i++){
							if(i === 0){
								frame(pList[0], mouse)
							}else if(i === 1){
								frame(pList[i], pList[i - 1],20)
							}else{
								frame(pList[i], pList[i - 1])
							}
						}
					}
					let last = pList[pList.length - 1]
					this.draw(() => {
						calc()
						calc()
						this.cx.beginPath()
						pList.forEach((p,i) => {
							(i > 0) && this.cx[i === 1 ? 'moveTo' : 'lineTo'](...p.pos)
						})
						this.cx.strokeStyle = "#fff"
						this.cx.lineWidth = 2
						this.cx.stroke()
						this.cx.closePath()
						this.cx.shadowBlur = 10
						this.cx.shadowColor = "#fff"
						this.cx.beginPath()
						this.cx.arc(...last.pos,10,0,Math.PI*2)
						this.cx.arc(...pList[1].pos,10,0,Math.PI*2)
						this.cx.closePath()
						this.cx.fill()
					},false,true)
					function frame(ch,pa,lmt){
						if(lmt === undefined){
							lmt = mdc.lmt
						}
						// retain
						let dis = getDis(ch.pos,pa.pos)
						let disAll = getAll(...dis)
						let retainAll = disAll > lmt ? (disAll - lmt) * mdc.k / 100 : 0
						if(disAll == 0){
							ch.retain[0] = 0
							ch.retain[1] = 0
						}else{
							let temp = getProj(retainAll,dis[0],dis[1])
							ch.retain[0] = temp[0]
							ch.retain[1] = temp[1]
						}
						// damp
						let stepAll = getAll(...ch.step)
						let dampDynamic = stepAll * mdc.dampM + mdc.dampAll
						if(stepAll == 0){
							ch.damp[0] = 0
							ch.damp[1] = 0
						}else{
							let temp = getProj(dampDynamic,ch.step[0],ch.step[1])
							ch.damp[0] = temp[0]
							ch.damp[1] = temp[1]
						}
						// mass
						if(disAll < 1){
							ch.step[0] = 0
							ch.step[1] = 0
						}else{
							ch.step[0] = ch.step[0] + ch.retain[0] - ch.damp[0]
							ch.step[1] = ch.step[1] + ch.retain[1] - ch.damp[1] + mdc.mass
						}
						ch.pos[0] = ch.pos[0] + ch.step[0]
						ch.pos[1] = ch.pos[1] + ch.step[1]
					}
					function getAll(a,b){
						return Math.sqrt(Math.pow(a,2) + Math.pow(b,2))
					}
					function getDis(p1,p2){
						return [p2[0] - p1[0], p2[1] - p1[1]]
					}
					function getProj(l,a,b){
						var all = getAll(a,b)
						if(all === 0){return [0,0]}
						return [l * a / all, l * b / all]
					}
					function getAngle(cross){
						var all = getAll(...cross)
						var halfAngle = all == 0 ? 0 : Math.asin(cross[1] / all) * 180 / Math.PI
						return cross[0] > 0 ? halfAngle + 90 : 270 - halfAngle
					}
				}
			})
		</script>
	</body>
</html>
